#ifndef PREDICTOR_H_SEEN
#define PREDICTOR_H_SEEN

#include <cstddef>
#include <cstring>
#include <inttypes.h>
#include <vector>
#include "op_state.h"   // defines op_state_c (architectural state) class
#include "tread.h"      // defines branch_record_c class

#define HISTORY_SIZE 12				// defines path history size in no. of bits
#define G_C_TABLE_SIZE (1 << HISTORY_SIZE)	// defines global and choice table sizes required for the no. of bits of history above

#define PC_SIZE 10				// defines PC size in no. of bits to be used as index for local table
#define L_TABLE_SIZE (1 << PC_SIZE)		// defines local table size required for the no. of bits of history above

#define L_COUNTER_STATES 8			// defines  local counter states
#define G_COUNTER_STATES 4			// defines global counter states
#define C_COUNTER_STATES 4			// defines choice counter states

#define L_COUNTER_NOT_TAKEN_STATES 4
#define G_COUNTER_NOT_TAKEN_STATES 2
#define C_COUNTER_NOT_TAKEN_STATES 2

// defines expression for global table initialization
#define G_TABLE_INIT_EXPR (3)
#define L_TABLE_INIT_EXPR (3)
#define C_TABLE_INIT_EXPR (7)
// defines decimal value to be loaded in path history
#define PATH_HISTORY_INIT_VAL 4095

//Define expression for global path history
#define SHIFT_AMOUNT	2
#define MASK_PC		((1 << PC_SIZE) -1)

enum TableType
{
Local,
Global,
Choice
};

//---------- Some results obtained with only global prediction------------------------------

//global_table[(cnt + block_base)] = 0;							//	-- FP 6.760
//global_table[(cnt + block_base)] = i; 						//	-- FP 6.763
//global_table[(cnt + block_base)] = ((G_COUNTER_STATES - 1) - i);			//	-- FP 6.877
//global_table[(cnt + block_base)] = (i%2);						//	-- FP 6.759
//global_table[(cnt + block_base)] = ((G_COUNTER_STATES - 1) - (i%2));			//	-- FP 6.884
//global_table[(cnt + block_base)] = ((i%2) + 1);					//	-- FP 6.860
//global_table[(cnt + block_base)] = (cnt%G_COUNTER_STATES);				//	-- FP 6.770
//global_table[(cnt + block_base)] = ((G_COUNTER_STATES -1) - (cnt%G_COUNTER_STATES));	//	-- FP 6.871
//global_table[(cnt + block_base)] = ((G_COUNTER_STATES/2) - 1);			//	-- FP 6.857

class PREDICTOR
{
public:
    bool get_prediction(const branch_record_c* br, const op_state_c* os);
    void update_predictor(const branch_record_c* br, const op_state_c* os, bool taken);
    void init_counters();
    uint get_PC_10bits(const branch_record_c* br);

    bool get_global_prediction(const branch_record_c* br, const op_state_c* os);
    bool get_local_prediction(const branch_record_c* br, const op_state_c* os);
    bool get_choice_prediction(const branch_record_c* br, const op_state_c* os);

    void update_global_predictor(const branch_record_c* br, const op_state_c* os, bool taken);
    void update_local_predictor(const branch_record_c* br, const op_state_c* os, bool taken);
    void update_choice_predictor(const branch_record_c* br, const op_state_c* os, bool actual_taken);
    void update_path_history(bool actual_taken);

    uint8_t read_table(TableType type, uint32_t index);
    void write_table(TableType type, uint32_t index, uint8_t value);
    uint8_t evaluate_updation_value(TableType type, uint8_t value, bool actual_taken, bool global_p, bool local_p);
};

#endif // PREDICTOR_H_SEEN

